package zeroj.web.dispatch;

import java.io.IOException;
import java.util.List;

import javax.servlet.FilterConfig;
import javax.servlet.ServletException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import zeroj.exceptions.ValidateException;
import zeroj.web.action.ActionHelper;
import zeroj.web.action.IAction;
import zeroj.web.core.ActionSupport;
import zeroj.web.core.Interceptor;
import zeroj.web.filter.FilterDispatcher;
import zeroj.web.view.FreemarkerView;
import zeroj.web.view.wrapper.HttpRequestWrapper;
import zeroj.web.view.wrapper.HttpResponseWrapper;

/**
 * 
 * 
 * @author lyl(lylsir at gmail dot com)
 * 
 * @since 1.0
 * 
 * 2008-9-9
 */
public final class Dispatcher {

	private static final Log log = LogFactory.getLog(Dispatcher.class);
	private static Dispatcher instance = null;
	private static ThreadLocal<HttpRequestWrapper> requestLocal = new ThreadLocal<HttpRequestWrapper>();
	private static ThreadLocal<HttpResponseWrapper> responseLocal = new ThreadLocal<HttpResponseWrapper>();
	
	private FilterConfig filterConfig;
	private FreemarkerView view;
	private ActionMapper actionMapper;
	
	public static Dispatcher get() {
		return instance;
	}
	public static Dispatcher get(FilterConfig config) {
		
		if(instance == null)
			instance = new Dispatcher(config);
		return instance;
	}
	private Dispatcher(FilterConfig config) {
		
		this.filterConfig = config;
		actionMapper = new ActionMapper(config.getInitParameter(FilterDispatcher.ActionPackageName));
		view = new FreemarkerView(config.getServletContext());
		init();
	}
	private void init() {
		
		log.debug("init ...");
		actionMapper.init();
		view.init();
	}
	public FilterConfig getFilterConfig() {
		return filterConfig;
	}
	public void service(HttpRequestWrapper request, HttpResponseWrapper response, ActionMapping mapping) throws ServletException {
		
		requestLocal.set(request); responseLocal.set(response);
		ActionInfo info = mapping.getInfo();
		try {
			String resultKey = preIntercept(mapping);
			if(resultKey == null) {
				if(info.isGetDirect() && request.isGet())
					resultKey = ActionSupport.INPUT;
				else {
					IAction action = ActionHelper.prepareAction(info);
					resultKey = action.execute();
				}
				String newKey = postIntercept(mapping, resultKey);
				if(newKey != null)
					resultKey = newKey;
				if(resultKey == null)
					return;
			}
			String page = info.getResult(resultKey);
			if(page == null)
				page = info.getPath();
			showView(page);
		} catch(ValidateException ve) {
			log.info("validate exception " + ve.getFieldName());
			
		} catch (Exception e) {
			log.warn("action execute", e);
			throw new ServletException("action execute", e);
		}  finally {
			requestLocal.remove(); responseLocal.remove();
		}
	}
	
	public void destroy() {
		
	}
	
	public ActionMapping parseMapping(String path) {
		
		if(path.endsWith("/"))
			path = path.substring(0, path.length() - 1);
		path = path.toLowerCase();
		return actionMapper.getActionMapping(path);
	}
	
	public FreemarkerView getView() {
		return view;
	}
	public ActionMapper getActionMapper() {
		return actionMapper;
	}
	private void showView(final String page) throws IOException, ServletException {
		
		view.display(page);
	}
	private String preIntercept(ActionMapping mapping) {
		
		String resultKey = null;
		List<Interceptor> interceptors = mapping.getInfo().getInterceptors();
		for(Interceptor interceptor : interceptors) {
			resultKey = interceptor.preExcute(mapping);
			if(resultKey != null)
				break;
		}
		return resultKey;
	}
	private String postIntercept(ActionMapping mapping, String resultKey) {
		
		String newKey = null;
		List<Interceptor> interceptors = mapping.getInfo().getInterceptors();
		for(int i = interceptors.size() - 1; i >= 0; --i) {
			newKey = interceptors.get(i).postExcute(mapping, resultKey);
			if(newKey != null)
				break;
		}
		return newKey;
	}
	public static HttpRequestWrapper getRequest() {
		
		return requestLocal.get();
	}
	public static HttpResponseWrapper getResponse() {
		
		return responseLocal.get();
	}
	
}
